//
//  LoginController.swift
//  hello
//
//  Created by yafei li on 2025/9/17.
//

import Vapor
import Fluent 
import JWT

struct LoginController: RouteCollection {
    func boot(routes: any Vapor.RoutesBuilder) throws {
        let loginRoutes = routes.grouped("api","login")
        loginRoutes.post("register", use: registerUser)
        loginRoutes.post("loginWithPwd", use: loginWithPwd)
        loginRoutes.post("refreshToken", use: refreshToken)
        loginRoutes.post("loginOut", use: loginOut)
    }

    //    注册用户
    func registerUser(req: Request) async throws -> ApiResponse<TokenResponseDTO> {
        let userDTO = try req.content.decode(RegisterDTO.self)
        // 验证必要参数
        guard let phone = userDTO.phone, !phone.isEmpty, let password = userDTO.password, !password.isEmpty else {
            throw Abort.requestParametersError
        }
        // 检查用户是否已存在
        if let _ = try await User.query(on: req.db).filter(\.$phone == phone).first() {
            throw Abort.userExist
        }
        
        // 注册新用户
        let newUser = try User(name: "", age: 0, phone: phone, password: password)
        
        try await newUser.save(on: req.db)
        
        // 查找用户
        if let u = try await User.query(on: req.db).filter(\.$phone == phone).first() {
           
            let refreshTokenService = RefreshTokenService()
            // 创建刷新令牌
            let (rawToken, tokenHash) = refreshTokenService.generateToken()
            // 保存刷新令牌
            try await refreshTokenService.save(tokenHash: tokenHash, userId:u.id!, on: req)
            //  过期时间
            let expiresInSeconds: TimeInterval = 60 * 60 * 24 * 1 // 1天
            // 生成JWT令牌
            let accessToken  =  try await generateToken(for: u, expiresInSeconds: expiresInSeconds, req: req)
            
            // 返回登录成功响应
            let resultDTO = TokenResponseDTO(accessToken: accessToken, refreshToken: rawToken, expiresInSeconds: expiresInSeconds)
            
            return ApiResponse.success(data: resultDTO, message: "注册成功")
        }else{
            throw Abort.operationFailed
        }
    }

    
    // 登录
    func loginWithPwd(req: Request) async throws -> ApiResponse<TokenResponseDTO> {
        let loginRequest = try req.content.decode(LoginDTO.self)
        guard let phone = loginRequest.phone,!phone.isEmpty, let password = loginRequest.password,!password.isEmpty else {
            throw Abort.requestParametersError
        }
        
        // 查找用户
        guard let user = try await User.query(on: req.db).filter(\.$phone == phone).first() else {
            throw Abort.userNotExist
        }
        // 验证密码
        let result = try user.verify(password: password)
        if  !result {
            throw Abort.passwordError
        }
        
        let refreshTokenService = RefreshTokenService()
        // 创建刷新令牌
        let (rawToken, tokenHash) = refreshTokenService.generateToken()
  
        // 保存刷新令牌
        try await refreshTokenService.save(tokenHash: tokenHash, userId: user.id!, on: req)
        
        //  过期时间
        let expiresInSeconds: TimeInterval = 60 * 60 * 24 * 1 // 1天
        
        // 生成JWT令牌
        let accessToken  =  try await generateToken(for: user, expiresInSeconds: expiresInSeconds, req: req)
        
        // 返回登录成功响应
        let resultDTO = TokenResponseDTO(accessToken: accessToken, refreshToken: rawToken, expiresInSeconds: expiresInSeconds)
        return ApiResponse.success(data: resultDTO, message: "登录成功")
    }
    
    // 刷新令牌
    func refreshToken(req: Request) async throws -> ApiResponse<TokenResponseDTO> {
        let refreshRequestDTO = try req.content.decode(RefreshRequestDTO.self)
        guard let refreshToken = refreshRequestDTO.refreshToken, !refreshToken.isEmpty else {
            throw Abort.requestParametersError
        }
        let tokenHash = SHA256.hash(data: Data(refreshToken.utf8)).compactMap { String(format: "%02x", $0) }.joined()
         
        let tokenService = RefreshTokenService()
         
        // 🔥 原子操作：获取并删除（防重放）
        guard let userID = try await tokenService.consume(tokenHash: tokenHash, on: req) else {
            throw Abort.tokenInvalid
        }
        
        // 查找用户
        guard let user = try await User.find(userID, on: req.db) else {
            throw Abort.userNotExist
        }
        
        // 创建新的刷新令牌
        let (newRawToken, newTokenHash) = tokenService.generateToken()
        // 保存新的刷新令牌
        try await tokenService.save(tokenHash: newTokenHash, userId: user.id!, on: req)
        //  过期时间
        let expiresInSeconds: TimeInterval = 60 * 60 * 24 * 1 // 1天
        // 生成新的JWT令牌
        let newAccessToken  =  try await generateToken(for: user, expiresInSeconds: expiresInSeconds, req: req)
        // 返回新的令牌
        let resultDTO = TokenResponseDTO(accessToken: newAccessToken, refreshToken: newRawToken, expiresInSeconds: expiresInSeconds)
        
        return ApiResponse.success(data: resultDTO, message: "刷新令牌成功")
    }
    
    // 退出登录
    func loginOut(req: Request) async throws -> ApiResponse<EmptyContent>{
        let user =  try await UserPayload.getCurrentUserFromToken(req: req)
        let token = req.headers.bearerAuthorization!.token
//        print("token:",token)
        let tokenHash = SHA256.hash(data: Data(token.utf8)).description.base64String()
        //  存入Token黑名单
        let tokenBlackList = TokenBlackList()
        try await tokenBlackList.save(tokenHash: tokenHash, userId: user.id!, on: req)
        print("用户 \(user.name) 退出登录")
        return ApiResponse.success()
    }
    

//    MARK: - Helper Methods
//    生成JWT令牌
    private func generateToken(for user: User,expiresInSeconds: TimeInterval = 0, req: Request) async throws -> String {
        let payload: UserPayload
        if expiresInSeconds > 0 {
            payload =  UserPayload(id: user.id!, name: user.name, phone: user.phone, exp: ExpirationClaim.init(value: Date().addingTimeInterval(expiresInSeconds)))
        }else{
            //   不设置过期
            payload =  UserPayload(id: user.id!, name: user.name, phone: user.phone, exp: ExpirationClaim.init(value: .distantFuture))
        }
        let token = try await req.jwt.sign(payload)
        return token
    }
  
}
